/******************************************************************************
*
*   Filename    : dlcd.c
*   Author      : Ariff Ehsan
*   Date        : Oct. 27, 2009
*   Description : Driver for Primer2 LCD.
*                 Code is modified from Cirlce LCD API for freeRTOS
*                 implementation.
*
******************************************************************************/

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"

#include "dlcd.h"
#include "dlcd_config.h"
#include "dlcd_ascii.h"

/******************************************************************************
*
*                            PROTOTYPE FUNCTIONS
*
******************************************************************************/

static void LCD_ST7732S_Controller_init( void );
static void LCD_CheckLCDStatus( void );
void LCD_Init( void );

static void LCD_CtrlLinesWrite( GPIO_TypeDef* GPIOx, u32 CtrlPins, BitAction BitVal );
static void LCD_CtrlLinesConfig( void );
void LCD_SendLCDCmd( u8 Cmd );
void LCD_SendLCDData( u8 Data );
u32 LCD_ReadLCDData( void );
static void LCD_DataLinesWrite( GPIO_TypeDef* GPIOx, u32 PortVal );
static void LCD_DataLinesConfig( u8 Mode );

void LCD_SetRect_For_Cmd( s16 x, s16 y, s16 width, s16 height );
static void LCD_DisplayRotate( Rotate_H12_V_Match_TypeDef H12 );
static void LCD_DrawChar( u8 x, u8 y, u8 width, const u8* bmp, u16 textColor, u16 bGndColor, u16 charMagniCoeff );

/******************************************************************************
*
*                            LOCAL VARIABLES
*
******************************************************************************/

xSemaphoreHandle lcd_xSemaphore;

Rotate_H12_V_Match_TypeDef   CurrentScreenOrientation;

static int CharFilter_XMin = 0;         //restricted area when painting characters
static int CharFilter_XMax = 128;       //restricted area when painting characters
static int CharFilter_YMin = 0;         //restricted area when painting characters
static int CharFilter_YMax = 160;       //restricted area when painting characters

u8* CurrentFont = (u8*) &AsciiDotsTable;           // pointer on the current font

u16 lcd_textColor, lcd_textBackColor;

/******************************************************************************
*
*                            PUBLIC FUNCTIONS
*
******************************************************************************/

void LCD_Configuration( void )
    {
    CurrentScreenOrientation = V12;
    lcd_xSemaphore = xSemaphoreCreateMutex();
    
    lcd_textColor = RGB_BLACK;
    lcd_textBackColor = RGB_WHITE;
    
    LCD_Init();
    }

void LCD_GetResource( void )
    {
    xSemaphoreTake( lcd_xSemaphore, portMAX_DELAY );
    }

void LCD_ReleaseResource( void )
    {
    xSemaphoreGive( lcd_xSemaphore );
    }

void LCD_PutChar( u8 x, u8 y, u8 chr )
    {
    LCD_DrawChar( x, y, CHAR_WIDTH, (u8*)&CurrentFont[ (chr-32) * CHAR_HEIGHT ],
                  lcd_textColor, lcd_textBackColor, 1 );
    }

void LCD_PutString( u8 x, u8 y, const u8* str, u8 len )
    {
    u8 i;
    
    for ( i = 0; i < len - 1; i++ )
        {
        LCD_PutChar( x, y, str[i] );
        x += CHAR_WIDTH;
        }
    }

void LCD_SetTextColor( u16 textColor, u16 backColor )
    {
    lcd_textColor = textColor;
    lcd_textBackColor = backColor;
    }

void LCD_GetTextColor( u16* textColor, u16* backColor )
    {
    if (textColor != NULL)
        *textColor = lcd_textColor;
    
    if (backColor != NULL)
        *backColor = lcd_textBackColor;
    }

void LCD_DrawFilledRect( u8 x, u8 y, u8 width, u8 height, u16 color )
    {
    u8 Line;
    u8 Column;

    /* Select LCD screen area. */
    LCD_SetRect_For_Cmd( x, y, width, height );

    /* Send LCD RAM write command. */
    LCD_SendLCDCmd( ST7732_RAMWR );

    /* Fill selected LCD screen area with provided color. */
    for( Line = 0; Line < width; Line++ )
        {
        for( Column = 0; Column < height; Column++ )
            {
            LCD_SendLCDData( color & 0xff );
            LCD_SendLCDData( ( color >> 8 ) & 0xff );
            }
        }
    }

void LCD_DrawRect( u8 x, u8 y, u8 width, u8 height, u16 color )
    {
    // Draw horizontal sides.
    LCD_DrawFilledRect( x, y,              width, 1, color );
    LCD_DrawFilledRect( x, y + height - 1, width, 1, color );

    // Draw vertical sides.
    LCD_DrawFilledRect( x,              y, 1, height, color );
    LCD_DrawFilledRect( x + width - 1,  y, 1, height, color );
    }

void LCD_ReadRect( u16 x, u16 y, u16 width, u16 height, u8* bmp )
    {
    int i;
    int bytesize = (width * height) *2; // 2 bytes per pixel.

    /* Select LCD screen area. */
    LCD_SetRect_For_Cmd( x, y, width, height );

    /* Restore 18 bit color mode */
    LCD_SendLCDCmd( ST7732_COLMOD );
    LCD_SendLCDData( 0x66 );

    /* Send LCD RAM read command. */
    LCD_SendLCDCmd( ST7732_RAMRD );

    // First read byte is dummy!
    LCD_ReadLCDData();

    // Read pixels from LCD screen.
    for( i = 0; i < bytesize; i++ )
        {
        u16 red_val  =   LCD_ReadLCDData() ;
        u16 byte0 = red_val & 0xf8;   //red: keep only 5 bits 
        u16 green_val = LCD_ReadLCDData();
        *bmp++ = (byte0 | ((green_val>>5) & 0x7) );
        u16 blue_val =  LCD_ReadLCDData();
        *bmp++ = ( ( (blue_val>>3) & 0x1F ) | ((green_val<<3)&0xE0) );
        i++; //don't multiply by two...
        }
    
    /* Restore 16 bit color mode */
    LCD_SendLCDCmd( ST7732_COLMOD );
    LCD_SendLCDData( 0x55 );
    }

void LCD_DrawPixel( u8 x, u8 y, u16 Color )
    {
    /* Select LCD screen area. */
    LCD_SetRect_For_Cmd( x, y, 1, 1 );

    /* Send LCD RAM write command. */
    LCD_SendLCDCmd( ST7732_RAMWR );

    // Draw pixel.
    LCD_SendLCDData( Color );
    LCD_SendLCDData( Color >> 8 );
    }

u16 LCD_GetPixel( u8 x, u8 y )
    {
    u16 val;

    LCD_ReadRect( x, y, 1, 1, (u8*)&val );

    return val;
    }


/******************************************************************************
*
*                            PRIVATE FUNCTIONS
*
******************************************************************************/

void LCD_Init( void )
    {
    //LCD_SetBackLight( UTIL_ReadBackupRegister( BKP_BKLIGHT ) );

    /* Do some gpio configs*/
    GPIO_InitTypeDef GPIO_InitStructure;

    /* Enable GPIO clock for LCD */
    RCC_APB2PeriphClockCmd( LCD_CTRL_PERIPH, ENABLE );
    RCC_APB2PeriphClockCmd( LCD_DATA_PERIPH, ENABLE );
    RCC_APB2PeriphClockCmd( LCD_CS_PERIPH, ENABLE );

    /* Enable GPIOC clock */
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC, ENABLE );

    /* Init BackLight*/
    //LCD_BackLightConfig();

    /* Configure control lines signals as output mode */
    LCD_CtrlLinesConfig();

    /* Configuration sequence */
    LCD_ST7732S_Controller_init();

    }

static void LCD_CtrlLinesConfig( void )
    {
    GPIO_InitTypeDef GPIO_InitStructure;
    
    GPIO_InitStructure.GPIO_Pin   = LCD_CTRL_PINS;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;

    GPIO_Init( LCD_CTRL_GPIO, &GPIO_InitStructure );

    GPIO_InitStructure.GPIO_Pin   = LCD_CS_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;

    GPIO_Init( LCD_CS_GPIO, &GPIO_InitStructure );

    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_RS_PIN,  Bit_SET );    /* RS = 1   */
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_RD_PIN,  Bit_SET );    /* RD = 1   */
    LCD_CtrlLinesWrite( LCD_CS_GPIO,   LCD_CS_PIN,  Bit_SET );    /* CS = 1   */
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_WR_PIN,  Bit_SET );    /* WR = 1   */
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_RST_PIN, Bit_RESET );  /* RST = 0  */
    }

void LCD_ST7732S_Controller_init( void )				
    {
    int i;
    
    const portTickType xDelay10 = 10 / portTICK_RATE_MS;
    const portTickType xDelay20 = 20 / portTICK_RATE_MS;
    const portTickType xDelay100 = 100 / portTICK_RATE_MS;
    const portTickType xDelay150 = 150 / portTICK_RATE_MS;
    const portTickType xDelay180 = 180 / portTICK_RATE_MS;
    const portTickType xDelay200 = 200 / portTICK_RATE_MS;
    const portTickType xDelay500 = 500 / portTICK_RATE_MS;

    /** Apply hardware reset **/
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_RST_PIN, Bit_SET );    /* RST = 1  */
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_RST_PIN, Bit_RESET );  /* RST = 0  */
    vTaskDelay(xDelay10);  //starting_delay( 0x500 );

    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_RST_PIN, Bit_SET );    /* RST = 1  */
    vTaskDelay(xDelay10);  //starting_delay( 0x500 );

    //default mode is output
    LCD_DataLinesConfig( 0 );

    LCD_CheckLCDStatus();
    
#define WriteCOM LCD_SendLCDCmd
#define WriteData LCD_SendLCDData

#define WRITE_LCD(addr,val)   { WriteCOM(val); }

    vTaskDelay(xDelay100);  //delayms(100);
    GPIO_WriteBit( GPIOD, LCD_RST_PIN, Bit_RESET ); // RST = 0
    
    vTaskDelay(xDelay100);  //delayms(100);
    GPIO_WriteBit( GPIOD, LCD_RST_PIN, Bit_SET );   // RST = 1
    
    vTaskDelay(xDelay100);  //delayms(100);

    WriteCOM(ST7732_SWRESET);          //Software Reset
    vTaskDelay(xDelay180);  //delayms(180);                      //DELAY 150MS

    WriteCOM(ST7732_SLPIN);                    //sleep IN
    vTaskDelay(xDelay100);  //delayms(100);
    
    WriteCOM(ST7732_SLPOUT);                    //sleep out
    vTaskDelay(xDelay150);  //delayms(150);

    WriteCOM(ST7732_FRMCTR1);          //	 frame
    WriteData(0x06);
    WriteData(0x03);
    WriteData(0x02);  
    vTaskDelay(xDelay10);    //delayms(10);

    WriteCOM(ST7732_INVCTR);           //	0xb4
    WriteData(0x03);

    WriteCOM(ST7732_DISSET5);          //  frame 0xB6
    WriteData(0x02);
    WriteData(0x0e);

    WriteCOM(ST7732_DISPCTRL);         // 0xF5
    WriteData(0x1a);

    WriteCOM(ST7732_PWCTR1);                    //Power Control 1 ( gvdd vci1)
    WriteData(0x02);
    WriteData(0x00);

    WriteCOM(ST7732_PWCTR2);                    //Power Control 2( avdd vcl vgh vgl)	
    WriteData(0x05);

    WriteCOM(ST7732_PWCTR3);                    //Power Control 3 ( in normal)   
    WriteData(0x02);
    WriteData(0x02);

    WriteCOM(ST7732_PWCTR4);                    //Power Control 4 ( in Idle mode)   
    WriteData(0x01);
    WriteData(0x02);

    WriteCOM(ST7732_PWCTR5);                    //Power Control 5 ( in partial mode)  
    WriteData(0x01);
    WriteData(0x02);

    WriteCOM(ST7732_VMCTR1);                    //Vcom Control ( vcomh,voml)  
    WriteData(0x47);
    WriteData(0x3a);

    WriteCOM(ST7732_OSCADJ);           // 0xF2 intern OSC 80Hz
    WriteData(0x02);

    WriteCOM(ST7732_DEFADJ);           //0xF6
    WriteData(0x4c);

    WriteCOM(0xf8);   //cmd 0xf8,dat 0x06; LOAD 
    WriteData(0x06);  //SLEEP OUT LOAD DEFAULT

    /****************gamma adjust **********************/
    WriteCOM(ST7732_GAMCTRP1);    		           //gamma
    WriteData(0x06);
    WriteData(0x1c);
    WriteData(0x1f);
    WriteData(0x1f);
    WriteData(0x18);
    WriteData(0x13);
    WriteData(0x06);
    WriteData(0x03);
    WriteData(0x03);
    WriteData(0x04);
    WriteData(0x07);
    WriteData(0x07);
    WriteData(0x00);

    WriteCOM(ST7732_GAMCTRN1);    		           //gamma
    WriteData(0x0a);
    WriteData(0x14);
    WriteData(0x1b);
    WriteData(0x18);
    WriteData(0x12);
    WriteData(0x0e);
    WriteData(0x02);
    WriteData(0x01);
    WriteData(0x00);
    WriteData(0x01);
    WriteData(0x08);
    WriteData(0x07);
    WriteData(0x00);

    WriteCOM(ST7732_MADCTR);           // Memory Access Control 0x36  
    WriteData(V12_MADCTRVAL);

    WriteCOM(ST7732_CASET);    	               //Column Range
    WriteData(0x00);
    WriteData(0x00);
    WriteData(0x00);
    WriteData(0x7F);

    WriteCOM(ST7732_RASET);    	               //page Range
    WriteData(0x00);
    WriteData(0x00);
    WriteData(0x00);
    WriteData(0x7F);                   // configured as 128x128

    WriteCOM(ST7732_COLMOD);           // Color mode = 65k 0x3A
    WriteData(0x55);

    WriteCOM(ST7732_TEON);             // 0x35
    WriteData(0x00);

    WriteCOM(ST7732_DISPON);                     //display on
    vTaskDelay(xDelay20);  //delayms(20);

    LCD_DrawFilledRect( 0,0,131,161, RGB_WHITE );

    }

static void LCD_CheckLCDStatus( void )
    {
    unsigned char ID1;
    unsigned char ID2;
    unsigned char ID3;

    // send command RDDID
    LCD_SendLCDCmd( ST7732_RDDID );

    /* Configure Data lines as Input */
    LCD_DataLinesConfig ( 1 );

    /* Start the LCD send data sequence */
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_RS_PIN, Bit_RESET );     /* RS = 0 */
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_RD_PIN, Bit_RESET );     /* RD = 0 */
    LCD_CtrlLinesWrite( LCD_CS_GPIO,   LCD_CS_PIN, Bit_RESET );     /* CS = 0 */
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_WR_PIN, Bit_SET );       /* WR = 1 */

    /* Read data to the LCD */
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_RD_PIN, Bit_SET );       /* RD = 1 */
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_RD_PIN, Bit_RESET );     /* RD = 0 */

    // Read only bits 7..14
    ID1 = (GPIO_ReadInputData( LCD_DATA_GPIO ) & LCD_DATA_PINS ) >> 7;

    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_RD_PIN, Bit_SET );       /* RD = 1 */
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_RD_PIN, Bit_RESET );     /* RD = 0 */

    // Read only bits 7..14
    ID2 = (GPIO_ReadInputData( LCD_DATA_GPIO ) & LCD_DATA_PINS ) >> 7;

    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_RD_PIN, Bit_SET );       /* RD = 1 */
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_RD_PIN, Bit_RESET );     /* RD = 0 */

    // Read only bits 7..14
    ID3 = (GPIO_ReadInputData( LCD_DATA_GPIO ) & LCD_DATA_PINS ) >> 7;
    
    LCD_DataLinesConfig( 0 );

    // Read LCD device IDs
    /*LCD_ReadLCDData();
    LCD_ReadLCDData();
    LCD_ReadLCDData();*/
    }

void LCD_SetRect_For_Cmd( s16 x, s16 y, s16 width, s16 height )
    {
    LCD_SendLCDCmd( ST7732_CASET );
    LCD_SendLCDData( 0 );
    LCD_SendLCDData( y + OrientationOffsetY[ CurrentScreenOrientation ] );
    LCD_SendLCDData( 0 );
    LCD_SendLCDData( y + OrientationOffsetY[ CurrentScreenOrientation ] + height - 1 );
    
    LCD_SendLCDCmd( ST7732_RASET );
    LCD_SendLCDData( 0 );
    LCD_SendLCDData( x + OrientationOffsetX[ CurrentScreenOrientation ] );
    LCD_SendLCDData( 0 );
    LCD_SendLCDData( x + OrientationOffsetX[ CurrentScreenOrientation ] + width - 1 );
    }

static void LCD_DisplayRotate( Rotate_H12_V_Match_TypeDef H12 )
    {
    // Memory Access Control 0x36
    LCD_SendLCDCmd( ST7732_MADCTR );

    switch( H12 )
        {
        case V3  :
            LCD_SendLCDData( V3_MADCTRVAL );
            break;

        case V6  :
            LCD_SendLCDData( V6_MADCTRVAL );
            break;

        case V9  :
            LCD_SendLCDData( V9_MADCTRVAL );
            break;

        case V12 :
            default  :
            LCD_SendLCDData( V12_MADCTRVAL );
            break;
        }
    }

static void LCD_DrawChar( u8 x, u8 y, u8 width, const u8* bmp, u16 textColor, u16 bGndColor, u16 charMagniCoeff )
    {
    int i, i0 = 0, i1 = 7;
    int j;
    int k1;
    int k2;
    
    #define MAX(i,j) (((i)>(j))?(i):(j))
    #define MIN(i,j) (((i)<(j))?(i):(j))
    
    int x0 = MAX(x, CharFilter_XMin);
    int y0 = MAX(y, CharFilter_YMin);
    
    static int width0, height0 ;
 
    width0 = MIN(CHAR_WIDTH * charMagniCoeff,  CharFilter_XMax - x0 );
    height0 = MIN(CHAR_HEIGHT * charMagniCoeff,  CharFilter_YMax - y0 );
    
    if ( y0 > y ) 
        height0 -= (y0-y);

    if ( x0 > x ) 
        width0 -= (x0-x);
        
    static int lin0;
    static int lin1;
        
    if ( (width0 <= 0) || (height0 <= 0) ) 
        return;
        
        
    // Select the area for LCD output.
    LCD_SetRect_For_Cmd( x0, y0, width0, height0);
    
    // Select LCD output mode.
    LCD_SendLCDCmd( ST7732_RAMWR );

    i0 = (x0 - x) / charMagniCoeff;
    i1 = width0 / charMagniCoeff;
    
    lin0 = (y0 - y) ;
    lin1 = height0 + lin0;

    for( i = i0; i < i1; i++ )
        {
        for( k1 = 0; k1 < charMagniCoeff; k1++ )
            {
            int n = 0;
            for( j = 0x80; j; j >>= 1 ) // 8
                {
                for( k2 = 0; k2 < charMagniCoeff; k2++ , n++)
                    {
                    if ( ( n < lin0 ) || (n>=lin1) ) continue;                           
    
                    LCD_SendLCDData( ( bmp[2*i] & j ) ? ( textColor & 255 ) : ( bGndColor &  255 ) );
                    LCD_SendLCDData( ( bmp[2*i] & j ) ? ( textColor >> 8  ) : ( bGndColor >> 8 ) );
                    }
                }

            for( j = 0x80; j > 2; j >>= 1 )  // 8
                {
                for( k2 = 0; k2 < charMagniCoeff; k2++ , n++)
                    {
                    if ( ( n < lin0 ) || (n>=lin1) ) continue;
                    LCD_SendLCDData( ( bmp[2*i+1] & j ) ? ( textColor & 255 ) : ( bGndColor & 255 ) );
                    LCD_SendLCDData( ( bmp[2*i+1] & j ) ? ( textColor >> 8  ) : ( bGndColor >> 8 ) );
                    }
                }
            }
        }
    }


/******************************************************************************
*
*                            CONTROL FUNCTIONS
*
******************************************************************************/

static void LCD_CtrlLinesWrite( GPIO_TypeDef* GPIOx, u32 CtrlPins, BitAction BitVal )
    {
    /* Set or Reset the control line */
    GPIO_WriteBit( GPIOx, CtrlPins, BitVal );
    }

void LCD_SendLCDCmd( u8 Cmd )
    {
    GPIO_InitTypeDef GPIO_InitStructure;
    
    /* Start the LCD send data sequence */
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_RS_PIN, Bit_RESET );     /* RS = 0 */
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_RD_PIN, Bit_SET );       /* RD = 1 */
    LCD_CtrlLinesWrite( LCD_CS_GPIO,   LCD_CS_PIN, Bit_RESET );     /* CS = 0 */
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_WR_PIN, Bit_RESET );     /* WR = 0 */

    /* Write data to the LCD */
    LCD_DataLinesWrite( LCD_DATA_GPIO, (u32)Cmd );
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_WR_PIN, Bit_SET );       /* WR = 1 */
    }

void LCD_SendLCDData( u8 Data )
    {
    /* Configure Data lines as Output */
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_RS_PIN, Bit_SET );
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_RD_PIN, Bit_SET );
    LCD_CtrlLinesWrite( LCD_CS_GPIO,   LCD_CS_PIN, Bit_RESET );
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_WR_PIN, Bit_RESET );

    /* Write data to the LCD */
    LCD_DataLinesWrite( LCD_DATA_GPIO,(u32)Data );
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_WR_PIN, Bit_SET );
    }

u32 LCD_ReadLCDData( void )
    {
    u32 LCDData = 0;

    /* Configure Data lines as Input */
    LCD_DataLinesConfig( 1 );

    /* Start the LCD send data sequence */
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO,  LCD_RS_PIN, Bit_SET );         /* RS = 1 */
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO,  LCD_WR_PIN, Bit_SET );         /* WR = 1 */
    LCD_CtrlLinesWrite( LCD_CS_GPIO,    LCD_CS_PIN, Bit_RESET );       /* CS = 0 */
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO,  LCD_RD_PIN, Bit_RESET );       /* RD = 0 */

    // Read only bits 7..14
    LCDData = (GPIO_ReadInputData( LCD_DATA_GPIO ) & LCD_DATA_PINS ) >> 7;
    
    LCD_CtrlLinesWrite( LCD_CTRL_GPIO, LCD_RD_PIN, Bit_SET );          /* RD = 1 */

    /* Read the LCD returned data */
    LCD_DataLinesConfig( 0 );

    return LCDData;
    }

static void LCD_DataLinesWrite( GPIO_TypeDef* GPIOx, u32 PortVal )
    {
    // Write only bits 7..14
    GPIOx->ODR = ( (GPIOx->ODR) & 0x807F ) | ((u8)PortVal << 7);
    }

static void LCD_DataLinesConfig( u8 Mode )
    {
    GPIO_InitTypeDef GPIO_InitStructure;
    
    GPIO_InitStructure.GPIO_Pin   =  LCD_DATA_PINS;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    if( Mode == 1 )
        {
        /* Configure D0~D7 lines as Input */
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
        }
    else
        {
        /* Configure D0~D7 lines in Output Push-Pull mode */
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
        }

    GPIO_Init( LCD_DATA_GPIO, &GPIO_InitStructure );
    }

